#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>

#include "mytbf.h"

typedef void (*sighandler_t)(int);

//创建一个包含MYTBF_MAX个指向mytbf_st结构体的指针数组
static struct mytbf_st* job[MYTBF_MAX];
static int inited = 0;

//备份alarm之前的行为
static sighandler_t alrm_handler_save;

struct mytbf_st
{
	int cps;
	int burst;
	int token;
	int pos;
};

/*要发生的行为：放令牌*/
static void alrm_handler(int s)
{
	int i;

	alarm(1);

	for(i = 0;i<MYTBF_MAX;i++)
	{
		//如果有任务存在,放cps个令牌
		if(job[i] !=NULL)
		{
			job[i]->token +=job[i]->cps;
			//不能多于用户给定的上限burst
			if(job[i]->token > job[i]->burst)
				job[i]->token = job[i]->burst;
		}
	}
}

/*程序结束要恢复行为*/
static void module_unload(void)
{
	signal(SIGALRM,alrm_handler_save);
	//关闭闹钟
	alarm(0);
	//把程序出错终止没来得及释放的内容释放
	for(int i = 0;i < MYTBF_MAX;i++)
		free(job[i]);
}

/*对信号注册行为*/
static void module_load(void)
{
	sighandler_t alrm_handler_save = signal(SIGALRM,alrm_handler);
	alarm(1);

	//挂一个钩子函数，当程序某一块出现问题或模块卸载的时候在exit前调用
	atexit(module_unload);
}

/*找能存放的位置*/
static int get_free_pos(void)
{
	int i;	
	for(i = 0;i < MYTBF_MAX;i++ )
	{
		if(job[i] == NULL)
				return i;
	}
	return -1;
}

/* cps----每秒钟传输的个数   burst----自定义要用到桶的上限*/
mytbf_t *mytbf_init(int cps,int burst)
{
	struct mytbf_st *me;
	int pos;
	
//定义构造函数，在初始化对象时这个功能被调用并且只会被调用一次
	if(!inited)
	{
		module_load();
		inited = 1;
	}



	pos = get_free_pos();  //找空位存放，并返回下标
	if(pos < 0)
		return NULL;  

	me = malloc(sizeof(*me));
	if(me == NULL)
		return NULL;

	me->token = 0;
	me->cps = cps;
	me->burst = burst;
	me->pos = pos;

	job[pos] = me; 

	return me;
		
}

static int min(int a,int b)
{
	if(a < b)
		return a;
	return b;
}

/*取令牌，从mytbf_t*里取int个，多了只能取现有的，返回实际取的个数*/
int mytbf_fetchtoken(mytbf_t *ptr,int size)
{
	struct mytbf_st *me = ptr;
	int n;

	if(size <= 0)
		return -EINVAL;
	
	while(me->token <= 0)
		pause();

	n = min(me->token,size);

	me->token -= n;
	return n;

}

/*多了没用完的还回mytbf_t*去,返回值是真正还了多少*/
int mytbf_returntoken(mytbf_t *ptr,int size)
{
	struct mytbf_st *me = ptr;

	if(size <= 0)
		return -EINVAL;

	me->token +=size;
	if(me->token > me->burst)
		me->token = me->burst;

	return size;
}


/*销毁令牌桶释放空间，返回是否成功*/
int mytbf_destroy(mytbf_t *ptr)
{
	struct mytbf_st *me = ptr;

	job[me->pos] = NULL;	
	free(ptr);

	return 0;
}
